﻿/*
 * Copyright 2011 Edward L. Stauff.                           Contact: <EdStauff@gmail.com>.
 * 
 * This file, "Enums.cs", is part of the "Stauffware.Common.Xml.Xerialization" 
 * library.  "Stauffware.Common.Xml.Xerialization" is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU Lesser Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or (at your option) 
 * any later version.
 * 
 * "Stauffware.Common.Xml.Xerialization" is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU Lesser Public License along with 
 * "Stauffware.Common.Xml.Xerialization".  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Xml;
using System.Reflection;
using System.Text;

namespace Stauffware.Common.Xml.Xerialization
{
	//=================================================================================
	/// <summary>
	/// Specifies what class members are to be included by default.
	/// (Individual class members can override this.)
	/// For properties, both the getter and the setter must match.
	/// </summary>

	[Flags]
	public enum MemberInclusion
	{
		/// <summary>
		/// No members are included (except those explicitly tagged otherwise).
		/// </summary>
		None = 0x00,

		/// <summary>
		/// Public members are included.  
		/// </summary>
		Public = 0x01,

		/// <summary>
		/// Internal (assembly) members are included.  
		/// </summary>
		Internal = 0x02,

		/// <summary>
		/// Protected (family) members are included.  
		/// </summary>
		Protected = 0x04,

		/// <summary>
		/// Private members are included.  
		/// </summary>
		Private = 0x08,

		/// <summary>
		/// All members are included (except those explicitly tagged 'None').
		/// This is a composit of Public, Internal, Protected, and Private.
		/// </summary>
		All = Public | Internal | Protected | Private, 
	}

	//=================================================================================
	/// <summary>
	/// Specifies where something lives in XML.
	/// </summary>

	public enum XmlType
	{
		/// <summary> The data is stored in an XML element. </summary>
		Element = 1,

		/// <summary> The data is stored in an XML attribute. </summary>
		Attribute = 2,

		/// <summary> The data is stored in XML text (entire contents of an element). </summary>
		Text = 3,		
	}

	//=================================================================================
	/// <summary>
	/// Specifies how a member and its value are encoded.
	/// </summary>

	public enum MemberValueEncoding
	{
		/// <summary>
		/// The member is xerialized as an XML attribute of the member's parent element.
		/// The member name is the attribute name, and the member value is the
		/// attribute value.  Thus, the member value type is implicit.
		/// </summary>
		/// <example>
		/// <![CDATA[		<person birth="12 May 1987"/>			]]>
		/// </example>
		Attribute,

		/// <summary>
		/// The member is xerialized as its own XML element, which encloses another
		/// XML element whose name identifies the value type and contains the value.
		/// </summary>
		/// <example>
		/// <![CDATA[		<person><birth><date>12 May 1987</date></birth></person>		]]>
		/// </example>
		/// <example>
		/// <![CDATA[	<person><birth><date d="12" m="May" y="1987"/></birth></person>		]]>
		/// </example>
		NestedElements,

		/// <summary>
		/// The member value has no XML element of its own; it is stored in the
		/// content and/or attributes of the member's XML element.  In other words,
		/// the member's XML name takes the place of the value type's XML name.
		/// The type of the value, therefore, must be exactly what the member
		/// expects, and not a subclass.
		/// (FUTURE: or the target type must have a suitable static factory method.)
		/// </summary>
		/// <example>
		/// <![CDATA[		<person><birth>12 May 1987</birth></person>		]]>
		/// </example>
		/// <example>
		/// <![CDATA[		<person><birth d="12" m="May" y="1987"/></person>		]]>
		/// </example>
		ImplicitValueType,

		/// <summary>
		/// The member has no XML element of its own; the value element is a direct
		/// child of the member's parent element.
		/// The member value type must not be used for any other member of the 
		/// member's class or ancestor classes.
		/// </summary>
		/// <example>
		/// <![CDATA[		<person><date>12 May 1987</date></person>		]]>
		/// </example>
		/// <example>
		/// <![CDATA[		<person><date d="12" m="May" y="1987"/></person>		]]>
		/// </example>
		ImplicitMemberName,

		/// <summary>
		/// The member value is xerialized as text content of the parent element.
		/// Both the member name and the value type are implicit.  This encoding
		/// cannot be used by more than one member of a class.
		/// </summary>
		/// <example>
		/// <![CDATA[		<person>12 May 1987</person>		]]>
		/// </example>
		Text,

		/// <summary>
		/// The member is xerialized by custom methods provided by the user,
		/// during the processing of XML attributes.
		/// </summary>
		CustomAttribute,

		/// <summary>
		/// The member is xerialized by custom methods provided by the user,
		/// during the processing of XML text and/or child elements.
		/// </summary>
		CustomContent,

		/// <summary>
		/// The member and its value are not stored at all.
		/// </summary>
		Omit,

		/// <summary>
		/// Apply the standard rules for determining member encoding:
		/// Attribute for strings, enums, and primitive value types;
		/// NestedElements for everything else.
		/// </summary>
		Automatic,

		/// <summary>
		/// Use the default specified in the next highest scoping level:
		/// XerialMember -> XerialType -> XerialContext.  At the XerialContext
		/// level, Default reverts to Automatic.
		/// </summary>
		Default,
	}

	//=================================================================================
	/// <summary> Extension functions for enum types.  </summary>

	public static class Enum_Extension_Methods
	{
		//-----------------------------------------------------------------------------
		/// <summary>
		/// Returns whether the field with the given FieldInfo is included by
		/// this MemberInclusion value.
		/// </summary>

		public static bool Includes (this MemberInclusion mi, FieldInfo fi)
		{
			// ELS 15 Sept 2014 - commented out these 2 lines; they broke a unit test
			//		involving a read-only field whose value is set by a constructor.
			//
			//// if the field is read-only, we never include it
			//if (fi.Attributes.HasFlag(FieldAttributes.InitOnly))
			//    return false;

			return (fi.IsPublic && mi.HasFlag(MemberInclusion.Public)) ||
					(fi.IsAssembly && mi.HasFlag(MemberInclusion.Internal)) ||
					(fi.IsFamily && mi.HasFlag(MemberInclusion.Protected)) ||
					(fi.IsPrivate && mi.HasFlag(MemberInclusion.Private));
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Returns whether the property with the given PropertyInfo is included by
		/// this MemberInclusion value.
		/// </summary>

		public static bool Includes (this MemberInclusion mi, PropertyInfo pi)
		{
			// if the property isn't read/write, we never include it
			if (!(pi.CanRead && pi.CanWrite))
				return false;

			if (mi == MemberInclusion.None) return false;
			if (mi == MemberInclusion.All) return true;

			MethodInfo getter = pi.GetGetMethod(true);
			MethodInfo setter = pi.GetSetMethod(true);

			if (getter.IsPublic && setter.IsPublic && !mi.HasFlag(MemberInclusion.Public))
				return false;

			if (getter.IsPrivate && setter.IsPrivate && !mi.HasFlag(MemberInclusion.Public))
				return false;

			// see if both getter and setter have compatible access levels
			if (!(Include(getter.Attributes, mi) && Include(setter.Attributes, mi)))
				return false;

			return true;
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Returns whether the given method attributes (for a property accessor) are
		/// compatible with the given member inclusion rules.
		/// </summary>

		private static bool Include (MethodAttributes methAttr, MemberInclusion inclusion)
		{
			if (methAttr.HasFlag(MethodAttributes.Assembly) && 
				inclusion.HasFlag(MemberInclusion.Internal))
				return true;

			if (methAttr.HasFlag(MethodAttributes.Family) && 
				inclusion.HasFlag(MemberInclusion.Protected))
				return true;

			return false;
		}

	}

	//=================================================================================
}
